﻿/**
 * Copyright (C) 2012 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject.Spi;
using NGuice.Inject.Internal.Util;

namespace NGuice.Inject.Internal
{
    //complete！
    /// <summary>
    /// C#无法使用泛型通配符，使用标记接口解决此问题
    /// </summary>
    public interface ILinkedBindingImpl : IBindingImpl
    {
        Key LinkedKey
        {
            get;
        }
    }

    public class LinkedBindingImpl<T> : BindingImpl<T>, ILinkedKeyBinding<T>, ILinkedBindingImpl, IHasDependencies
    {
        readonly Key targetKey;

        public LinkedBindingImpl(InjectorImpl injector, Key<T> key, object source,
            InternalFactory<T> internalFactory, Scoping scoping, Key targetKey)
            : base(injector, key, source, internalFactory, scoping)
        {
            this.targetKey = targetKey;
        }

        public LinkedBindingImpl(object source, Key<T> key, Scoping scoping, Key targetKey)
            : base(source, key, scoping)
        {
            this.targetKey = targetKey;
        }


        public override V AcceptTargetVisitor<V>(IBindingTargetVisitor<T, V> visitor)
        {
            return visitor.Visit((ILinkedKeyBinding<T>)this);
        }


        public Key LinkedKey
        {
            get
            {
                return targetKey;
            }
        }

        public BindingImpl<T> WithScoping(Scoping scoping)
        {
            return new LinkedBindingImpl<T>(Source, Key, scoping, targetKey);
        }

        public BindingImpl<T> WithKey(Key<T> key)
        {
            return new LinkedBindingImpl<T>(Source, key, Scoping, targetKey);
        }

        public override void ApplyTo(IBinder binder)
        {
            Scoping.ApplyTo(binder.WithSource(Source).Bind(Key).ToNoGenericKey(LinkedKey));
        }

        public override string ToString()
        {
            return new ToStringBuilder(typeof(ILinkedKeyBinding<T>))
                .Add("key", Key)
                .Add("source", Source)
                .Add("scope", Scoping)
                .Add("target", targetKey)
                .ToString();
        }

        public override bool Equals(object obj)
        {
            if (obj is LinkedBindingImpl<T>)
            {
                LinkedBindingImpl<T> o = (LinkedBindingImpl<T>)obj;
                return Key.Equals(o.Key)
                  && Scoping.Equals(o.Scoping)
                  && Objects.Equal(targetKey, o.targetKey);
            }
            else
            {
                return false;
            }
        }

        public int GetHashCode()
        {
            return Objects.GetHashCode(Key, Scoping, targetKey);
        }

        Key ILinkedBindingImpl.LinkedKey
        {
            get { return LinkedKey; }
        }

        ISet<Dependency> IHasDependencies.Dependencies
        {
            get 
            {
                return ImmutableSet<Dependency>.Of(Dependency<T>.GetForNoGeneric(targetKey));
            }
        }

    }
}
